// __BEGIN_LICENSE__
//  Copyright (c) 2006-2013, United States Government as represented by the
//  Administrator of the National Aeronautics and Space Administration. All
//  rights reserved.
//
//  The NASA Vision Workbench is licensed under the Apache License,
//  Version 2.0 (the "License"); you may not use this file except in
//  compliance with the License. You may obtain a copy of the License at
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// __END_LICENSE__


#include <test/Helpers.h>
#include <vw/Math/Vector.h>
#include <vw/Math/Matrix.h>
#include <vw/Math/LinearAlgebra.h>

using namespace vw;
using namespace vw::math;

TEST(LinearAlgebra, LeastSquaresLapackHang) {
  double hessian[64][64] = {
    {100,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0, 100,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0, 100,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0, 100,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,     52035903211.8207550048828125, -3428337940.53478860855102539062,  76606055.4305936545133590698242,  201112954.763717770576477050781, -360757219.056918501853942871094,  3516118919.04611873626708984375, -71856295.9016342014074325561523, -216727242.677746802568435668945,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,  -3428337940.5347881317138671875,         2304129589152.9697265625, -509759118.046770453453063964844, -1338264473.63572788238525390625,   2400584141.5771121978759765625,   -23397284575.55725860595703125,  478152827.724064886569976806641,  1442166516.25160503387451171875,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,  76606055.4305936545133590698242, -509759118.046770393848419189453,  1150445076.25349020957946777344,  29903458.8265066966414451599121, -53640943.5140964463353157043457,  522811260.211098074913024902344, -10684303.1988879628479480743408, -32225145.2454196996986865997314,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,  201112954.763717740774154663086, -1338264473.63572764396667480469,  29903458.8265066966414451599121,  7929024323.65255832672119140625, -140822923.015630573034286499023,  1372530105.27423834800720214844, -28049372.5181634537875652313232, -84600285.7289541959762573242188,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0, -360757219.056918501853942871094,   2400584141.5771121978759765625, -53640943.5140964463353157043457, -140822923.015630573034286499023,      336995472307.47540283203125,   4875847621.8385028839111328125,  245732476.937676340341567993164,  3575120134.79177808761596679688,  -3387598769.4252300262451171875,  -7162299064.1318492889404296875, -55236485.4167294353246688842773,    -3431240642.98232269287109375,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,  3516118919.04611825942993164062,  -23397284575.557254791259765625,  522811260.211098015308380126953,  1372530105.27423834800720214844,   4875847621.8385028839111328125,         4187041644539.6552734375,     -25427443.372287750244140625,  6666320913.04433536529541015625, -8060315543.30811977386474609375, -17041684804.4369182586669921875, -131427460.058298796415328979492,   -8164155252.705287933349609375,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0, -71856295.9016341865062713623047,   478152827.72406482696533203125, -10684303.1988879628479480743408, -28049372.5181634537875652313232,  245732476.937676370143890380859, -25427443.3722876310348510742188,  2262858412.96819734573364257812,  247149667.398677408695220947266, -214656295.487476140260696411133, -453841404.760226190090179443359, -3500077.82572045270353555679321, -217421676.969618946313858032227,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0, -216727242.677746802568435668945,  1442166516.25160503387451171875, -32225145.2454196996986865997314, -84600285.7289541959762573242188,  3575120134.79177808761596679688,  6666320913.04433536529541015625,  247149667.398677408695220947266,       393017700198.2322998046875,     -3760394875.0888519287109375,    -7950490754.01122283935546875, -61315111.6223340630531311035156, -3808839419.09581899642944335938,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0, -3387598769.42523050308227539062,     -8060315543.3081207275390625, -214656295.487476110458374023438, -3760394875.08885240554809570312,        916238666807.683349609375,    17805550101.09456634521484375, -23660528.8439779356122016906738,    5167426613.172054290771484375,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,   -5335120734.869167327880859375,  -9834805784.9388256072998046875, -385286702.085365235805511474609, -2092683774.08351945877075195312,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0, -7162299064.13185024261474609375,     -17041684804.436920166015625, -453841404.760226190090179443359, -7950490754.01122379302978515625,    17805550101.09456634521484375,         3544384439464.1044921875,    -28361405.7532672882080078125,    10566130999.84996795654296875,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,  -9909441707.9911403656005859375,   -18267146982.87990570068359375, -715630686.710729718208312988281, -3886946313.51169919967651367188,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0, -55236485.4167294353246688842773, -131427460.058298796415328979492, -3500077.82572045223787426948547, -61315111.6223340630531311035156, -23660528.8439779356122016906738,    -28361405.7532672882080078125,  234179715.423725873231887817383,  39415062.1700558736920356750488,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,  84091689.5222829431295394897461,  155015317.492961853742599487305,  6072854.08127158042043447494507,  32984691.9955160357058048248291,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0, -3431240642.98232316970825195312, -8164155252.70528888702392578125, -217421676.969618946313858032227, -3808839419.09581899642944335938,    5167426613.172054290771484375,    10566130999.84996795654296875,  39415062.1700558811426162719727,      422490306613.73321533203125,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0, -1394344027.16107916831970214844, -2570345337.24661159515380859375, -100695417.872392967343330383301, -546926914.335672736167907714844,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0, 100,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0, 100,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0, 100,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0, 100,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0, 100,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0, 100,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0, 100,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0, 100,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,           1426722443785.33984375,  11348878840.4476909637451171875,  571729777.707125186920166015625, -3947798073.17946290969848632812, -3917902.63891778513789176940918, -81468297.3408521711826324462891,  1542461.95929508702829480171204,  18688562.2456819936633110046387,   0,   0,   0,   0,   0,   0,   0,   0, -13978703741.9352855682373046875,   -11558763581.79933929443359375, -1752911678.19793677330017089844,   3290236944.1035976409912109375,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,  11348878840.4476909637451171875,        3230770400965.05126953125, -212557988.043796360492706298828,  -8409155361.9000186920166015625, -1105506693.75286173820495605469,  -22987745316.672344207763671875,  435233383.270312368869781494141,   5273313954.7656841278076171875,   0,   0,   0,   0,   0,   0,   0,   0, -11150133152.5391635894775390625,     -9219864401.9577789306640625,   -1398212522.232189178466796875,  2624462232.51023578643798828125,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,  571729777.707125186920166015625, -212557988.043796360492706298828,    4330790018.471668243408203125, -5912734.38734260201454162597656,  32227103.5804399512708187103271,  670125702.166744947433471679688, -12687676.5229729935526847839355, -153724654.941261172294616699219,   0,   0,   0,   0,   0,   0,   0,   0,    -568132652.433414459228515625, -469779682.995789349079132080078, -71243112.3515767455101013183594,  133724204.811654046177864074707,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,   -3947798073.179462432861328125,  -8409155361.9000186920166015625, -5912734.38734260201454162597656,     232575536066.046112060546875,  253546032.836815595626831054688,  5272199310.81513881683349609375, -99820017.6533732712268829345703, -1209425361.86285042762756347656,   0,   0,   0,   0,   0,   0,   0,   0,  3888222388.36966085433959960938,  3215108079.42782306671142578125,  487578144.428164899349212646484, -915190219.728730678558349609375,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   -5335120734.869167327880859375,   -9909441707.991138458251953125,  84091689.5222829580307006835938, -1394344027.16107916831970214844,   0,   0,   0,   0,   0,   0,   0,   0, -3917902.63891778560355305671692, -1105506693.75286173820495605469,  32227103.5804399512708187103271,  253546032.836815565824508666992,       1338772758344.756103515625,   -315797017.6686229705810546875,  1145011055.71792697906494140625,  3354611006.27231550216674804688,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,     -7845856826.0749053955078125,  11305959840.2995014190673828125, -1288795023.23526239395141601562, -628848035.648093819618225097656,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,  -9834805784.9388256072998046875,   -18267146982.87990570068359375,     155015317.492961883544921875, -2570345337.24661159515380859375,   0,   0,   0,   0,   0,   0,   0,   0, -81468297.3408521860837936401367,  -22987745316.672344207763671875,  670125702.166744947433471679688,  5272199310.81513881683349609375,   -315797017.6686229705810546875,         5761405395963.5087890625, -840290959.799969315528869628906, -3590908456.09719800949096679688,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,       11175191332.78399658203125,  -16103564877.225391387939453125,  1835686183.50624370574951171875,  895695304.336634039878845214844,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0, -385286702.085365176200866699219,   -715630686.7107295989990234375,  6072854.08127158042043447494507, -100695417.872392952442169189453,   0,   0,   0,   0,   0,   0,   0,   0,  1542461.95929508726112544536591,  435233383.270312428474426269531, -12687676.5229729954153299331665, -99820017.6533732712268829345703,  1145011055.71792697906494140625, -840290959.799969434738159179688,   11464825098.934902191162109375,  401370212.129423677921295166016,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0, -778139674.118347406387329101562,  1121307219.94413638114929199219, -127820652.558013007044792175293, -62368153.8392246663570404052734,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0, -2092683774.08351969718933105469, -3886946313.51169872283935546875,  32984691.9955160357058048248291, -546926914.335672736167907714844,   0,   0,   0,   0,   0,   0,   0,   0,  18688562.2456819973886013031006,  5273313954.76568508148193359375, -153724654.941261172294616699219, -1209425361.86285042762756347656,  3354611006.27231550216674804688, -3590908456.09719896316528320312,  401370212.129423677921295166016,     234436918854.249725341796875,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0, -1514389282.67150163650512695312,  2182250427.45643711090087890625, -248760258.313846617937088012695, -121378804.982721433043479919434,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0, 100,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0, 100,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0, 100,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0, 100,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0, 100,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0, 100,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0, 100,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0, 100,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,    -13978703741.9352874755859375,  -11150133152.539165496826171875, -568132652.433414578437805175781,   3888222388.3696613311767578125,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,         2850527813578.3935546875,  4622392458.34859371185302734375,  4494137203.90339756011962890625,   3237005147.4480762481689453125, -336276588.832778871059417724609, -2230925708.69995927810668945312,  7479014.07260653935372829437256,  809909543.788650035858154296875,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,  -14024547629.797252655029296875,       9575458898.694610595703125, -3718830163.84450864791870117188,  -6419475795.7882633209228515625,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,   -11558763581.79933929443359375,     -9219864401.9577789306640625, -469779682.995789408683776855469,  3215108079.42782306671142578125,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,  4622392458.34859180450439453125,         3686732628805.1865234375, -776329499.522964596748352050781,    -15056295817.1784515380859375, -3186711995.04144620895385742188,   -21141280576.91155242919921875,  70874585.5278993695974349975586,  7675076243.18525409698486328125,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,      8975821333.5864410400390625, -6128369379.92742443084716796875,  2380080698.62449169158935546875,  4108515249.06626033782958984375,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0, -1752911678.19793677330017089844,   -1398212522.232189178466796875, -71243112.3515767306089401245117,  487578144.428164899349212646484,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,  4494137203.90339756011962890625,      -776329499.5229644775390625,      78879043780.417083740234375,  1189257961.17744874954223632812,   62468556.967953637242317199707,  414428819.469490408897399902344, -1389342.08379006688483059406281, -150453175.020785003900527954102,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0, -2777932320.34110784530639648438,  1896672709.80391478538513183594, -736612600.898017287254333496094, -1271546845.11028838157653808594,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,  3290236944.10359716415405273438,  2624462232.51023578643798828125,  133724204.811654046177864074707, -915190219.728730678558349609375,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,  3237005147.44807720184326171875, -15056295817.1784496307373046875,  1189257961.17744851112365722656,       739198091121.6763916015625,  1153257058.23730301856994628906,  7650936477.29572963714599609375, -25649200.8493005633354187011719,  -2777576343.1824398040771484375,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   -7238002083.368152618408203125,  4941848627.66654872894287109375, -1919270495.14309430122375488281, -3313060813.83521938323974609375,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,     -7845856826.0749053955078125,       11175191332.78399658203125, -778139674.118347406387329101562, -1514389282.67150139808654785156,   0,   0,   0,   0,   0,   0,   0,   0, -336276588.832778871059417724609, -3186711995.04144620895385742188,  62468556.9679536446928977966309,  1153257058.23730301856994628906,      1003088486624.8897705078125, -2147223009.66246318817138671875,  1401574846.17712092399597167969, -1931974366.13233661651611328125,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0, -1605742820.34344959259033203125, -6022259877.47290325164794921875, -289048055.473094344139099121094,  977435500.953568100929260253906},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,   11305959840.299503326416015625, -16103564877.2253932952880859375,  1121307219.94413638114929199219,  2182250427.45643711090087890625,   0,   0,   0,   0,   0,   0,   0,   0, -2230925708.69995927810668945312,   -21141280576.91155242919921875,  414428819.469490408897399902344,  7650936477.29572963714599609375, -2147223009.66246509552001953125,         5926918840724.5888671875, -1452025732.99509596824645996094,  -13617856790.170688629150390625,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0, -5797279518.79589176177978515625,  -21742413169.920009613037109375, -1043562113.87794816493988037109,  3528875694.68324422836303710938},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0, -1288795023.23526263236999511719,  1835686183.50624394416809082031, -127820652.558013007044792175293, -248760258.313846617937088012695,   0,   0,   0,   0,   0,   0,   0,   0,  7479014.07260653935372829437256,  70874585.5278993695974349975586, -1389342.08379006711766123771667, -25649200.8493005670607089996338,  1401574846.17712092399597167969, -1452025732.99509596824645996094,         22335213240.511474609375,  17722769.5224673002958297729492,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0, -126357555.881047621369361877441, -473897830.559948205947875976562, -22745489.1026308573782444000244,   76915405.983533337712287902832},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0, -628848035.648093819618225097656,  895695304.336634159088134765625, -62368153.8392246663570404052734, -121378804.982721418142318725586,   0,   0,   0,   0,   0,   0,   0,   0,  809909543.788650035858154296875,  7675076243.18525409698486328125, -150453175.020785003900527954102,  -2777576343.1824398040771484375, -1931974366.13233661651611328125,  -13617856790.170688629150390625,  17722769.5224672853946685791016,      406667174005.38714599609375,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,  1373989087.58571481704711914062,  5153078842.65254306793212890625,  247330312.785055130720138549805,  -836364139.45903873443603515625},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,  480525170.144254267215728759766,  317903790.888737738132476806641,  31357120.0579313412308692932129,  115950534.328794106841087341309, -18910929.9191928021609783172607, -317102480.646451473236083984375, -26120407.6410762146115303039551, -117568622.526939362287521362305,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,  317903790.888737738132476806641,       2145448580290.332763671875,  2095256509.32735896110534667969,  7747717627.24241161346435546875, -1263612504.50996327400207519531,   -21188522270.88619232177734375,   -1745343769.936107635498046875,     -7855836925.9396820068359375,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,  31357120.0579313449561595916748,  2095256509.32735919952392578125,      20873680727.959564208984375,  764212691.938043832778930664062, -124639120.848011091351509094238, -2089975192.93774533271789550781, -172155714.102206021547317504883, -774877270.112313628196716308594,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,  115950534.328794106841087341309,  7747717627.24241161346435546875,  764212691.938043832778930664062,        285411984700.084228515625, -460883290.107587635517120361328, -7728188682.74100971221923828125, -636587384.333368062973022460938, -2865296090.41804933547973632812,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0, -18910929.9191928058862686157227, -1263612504.50996327400207519531, -124639120.848011091351509094238, -460883290.107587695121765136719,     39060729236.3231048583984375, -1470231842.72939562797546386719,  118681870.282590851187705993652,  395941394.834030330181121826172, -189710406.152065634727478027344,  2742007921.68966531753540039062,  -18507339.122667051851749420166,  73525541.9979507476091384887695,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0, -317102480.646451473236083984375,   -21188522270.88619232177734375, -2089975192.93774533271789550781,  -7728188682.7410106658935546875, -1470231842.72939562797546386719,          4551763535949.294921875,  1610728989.22468781471252441406,    8461562184.037593841552734375,  1662646554.39708781242370605469,   -24031312333.34931182861328125,  162200715.540588974952697753906, -644387366.737710833549499511719,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0, -26120407.6410762146115303039551,   -1745343769.936107635498046875, -172155714.102206021547317504883, -636587384.333368062973022460938,  118681870.282590836286544799805,  1610728989.22468781471252441406,   14555496082.485507965087890625,  642067403.172122716903686523438, -9046616.45028873160481452941895,  130756633.093158587813377380371, -882549.365394255495630204677582,  3506172.33522702986374497413635,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0, -117568622.526939362287521362305, -7855836925.93968105316162109375, -774877270.112313508987426757812, -2865296090.41804933547973632812,  395941394.834030270576477050781,    8461562184.037593841552734375,  642067403.172122597694396972656,      295084748585.39544677734375,  43457977.6162321940064430236816, -628126423.327586889266967773438,   4239575.1800994481891393661499, -16842889.2393338643014430999756,                                0,                                0,                                0,                                0},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,  -14024547629.797252655029296875,      8975821333.5864410400390625, -2777932320.34110784530639648438, -7238002083.36815357208251953125,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0, -189710406.152065634727478027344,  1662646554.39708781242370605469, -9046616.45028873160481452941895,  43457977.6162321940064430236816,             1483116544814.515625,  -14462509042.904636383056640625,  4037989192.30871295928955078125,    8509785517.623775482177734375, -523762703.531414628028869628906,  3333374467.47815370559692382812, -244503335.980549603700637817383, -2202781177.25504922866821289062},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,   9575458898.6946086883544921875,  -6128369379.9274234771728515625,  1896672709.80391478538513183594,  4941848627.66654872894287109375,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,  2742007921.68966531753540039062,  -24031312333.349308013916015625,  130756633.093158587813377380371, -628126423.327586889266967773438, -14462509042.9046344757080078125,         4733433222678.0400390625,    -4359786574.51195526123046875,  -14486319662.610271453857421875,  2576570239.31463527679443359375,  -16398024127.123332977294921875,  1202796637.96802973747253417969,   10836243945.532100677490234375},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0, -3718830163.84450864791870117188,  2380080698.62449169158935546875, -736612600.898017287254333496094, -1919270495.14309453964233398438,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,  -18507339.122667051851749420166,  162200715.540588974952697753906, -882549.365394255495630204677582,   4239575.1800994481891393661499,  4037989192.30871295928955078125,    -4359786574.51195526123046875,    116357113151.8227386474609375,  2798110884.17669200897216796875, -266420974.594706565141677856445,  1695578681.58013033866882324219, -124370858.452482283115386962891, -1120482814.27118778228759765625},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,  -6419475795.7882633209228515625,  4108515249.06626081466674804688, -1271546845.11028838157653808594, -3313060813.83521938323974609375,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,  73525541.9979507476091384887695, -644387366.737710714340209960938,  3506172.33522702986374497413635, -16842889.2393338643014430999756,    8509785517.623775482177734375,  -14486319662.610271453857421875,  2798110884.17669200897216796875,       1019212008690.475341796875, -1710234551.24502873420715332031,      10884417977.992218017578125, -798373099.629319429397583007812,  -7192708554.4147167205810546875},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0, -1605742820.34344959259033203125, -5797279518.79589176177978515625, -126357555.881047621369361877441,  1373989087.58571481704711914062,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0, -523762703.531414568424224853516,  2576570239.31463527679443359375, -266420974.594706565141677856445, -1710234551.24502897262573242188,      199648776915.87884521484375,  3282917205.88789606094360351562,  472956201.527055144309997558594,  762859594.998704791069030761719},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0, -6022259877.47290325164794921875,  -21742413169.920009613037109375, -473897830.559948205947875976562,  5153078842.65254306793212890625,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,  3333374467.47815370559692382812, -16398024127.1233310699462890625,  1695578681.58013033866882324219,      10884417977.992218017578125,  3282917205.88789606094360351562,         3898723015073.0029296875, -154486728.234182715415954589844, -14511258611.8310794830322265625},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0, -289048055.473094344139099121094, -1043562113.87794804573059082031, -22745489.1026308573782444000244,  247330312.785055130720138549805,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0, -244503335.980549573898315429688,  1202796637.96802973747253417969, -124370858.452482283115386962891, -798373099.629319429397583007812,  472956201.527055144309997558594, -154486728.234182834625244140625,   14121266882.852619171142578125,  629928590.854237914085388183594},
    {  0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,   0,   0,   0,   0,   0,   0,   0,   0,                                0,                                0,                                0,                                0,  977435500.953568220138549804688,   3528875694.6832447052001953125,   76915405.983533337712287902832, -836364139.459038853645324707031,                                0,                                0,                                0,                                0,                                0,                                0,                                0,                                0,  -2202781177.2550487518310546875,  10836243945.5320987701416015625, -1120482814.27118778228759765625,  -7192708554.4147167205810546875,  762859594.998704791069030761719, -14511258611.8310794830322265625,  629928590.854237794876098632812,       788253920298.1646728515625}
  };

  double del_J[64]={0, 0, 0, 0, -51879401.1206895336508750915527, 345220721.931542813777923583984, -7713941.33798606880009174346924, -20251317.2964757941663265228271, 60085977.6156569197773933410645, -297528532.086134672164916992188, 8741160.50715760141611099243164, 48197324.407920315861701965332, -15920009.9936054665595293045044, -36273690.7840106561779975891113, -585970.965214778785593807697296, -23774308.3563742823898792266846, 0, 0, 0, 0, 0, 0, 0, 0, -19533967.4717127159237861633301, 3536458.01012331061065196990967, -1355565.05823698081076145172119, 1055282.69741864409297704696655, -27092240.4667053557932376861572, -15049010.0509770475327968597412, -1959159.77428447338752448558807, -2677786.77639175020158290863037, 0, 0, 0, 0, 0, 0, 0, 0, 28170504.2359863072633743286133, 26931334.5266373977065086364746, 3543839.55987171921879053115845, -6475920.25083816982805728912354, 18734840.3757112547755241394043, -19931493.7801269963383674621582, 3073040.57909747073426842689514, 2494469.90114247798919677734375, 204364.908076941268518567085266, 13655492.0584293957799673080444, 1346938.65313535067252814769745, 4980631.39250720385462045669556, -2408244.79752033436670899391174, 365862.393878269940614700317383, -1198100.97325941827148199081421, -4684547.88798932544887065887451, -7755547.88419973477721214294434, -1090775.97667748015373945236206, -2621457.35733163449913263320923, -9007306.06561579741537570953369, -3699611.96560296509414911270142, -26897787.7221815399825572967529, -296697.075326256104744970798492, 8450466.71942384354770183563232};
  vw::Vector<double> del_J_vw(64);
  std::copy(&del_J[0], &del_J[64], del_J_vw.begin());

  vw::Matrix<double> hessian_vw(64,64);
  for (unsigned int i = 0; i < 64; i++) {
    for (unsigned int j = 0; j < 64; j++) {
      hessian_vw(i, j) = hessian[i][j];
    }
  }

  //TS_TRACE("About to call least_squares.  If this hangs, it may be due to a bug in lapack..");
  least_squares(hessian_vw, del_J_vw);
  //TS_TRACE("least_squares returned");
  // TODO: check the return value
}

TEST(LinearAlgebra, LeastSquaresStatic) {
  Matrix<float,4,2> A;
  A(0,0) = 4; A(0,1) = 17;
  A(1,0) = 32; A(1,1) = 33;
  A(2,0) = 63; A(2,1) = 3;
  A(3,0) = 2; A(3,1) = 73;

  Vector<float,4> b;
  b(0) = 5;
  b(1) = 95;
  b(2) = 2;
  b(3) = 1;

  Vector<float,2> x;

  // Find the least squares solution to the overconstrained problem Ax=b;
  x = least_squares(A,b);

  EXPECT_NEAR( 0.52625521713421449830, x[0], 1e-6 );
  EXPECT_NEAR( 0.37689005929142064427, x[1], 1e-6 );
}

TEST(LinearAlgebra, LeastSquaresUnderdetermined) {
  Matrix<float,2,4> A;
  A(0,0) = 4; A(1,0) = 17;
  A(0,1) = 32; A(1,1) = 33;
  A(0,2) = 63; A(1,2) = 3;
  A(0,3) = 2; A(1,3) = 73;

  Vector<float,2> b;
  b(0) = 5;
  b(1) = 95;

  Vector<float,4> x;

  // Find the least squares solution to the overconstrained problem Ax=b;
  x = least_squares(A,b);

  Vector<float,2> b_prime = A*x;

  EXPECT_NEAR( 5,  b_prime[0], 1e-4 );
  EXPECT_NEAR( 95, b_prime[1], 1e-4 );
}

TEST(LinearAlgebra, LeastSquaresDynamic) {
  Matrix<float> A(4,2);
  A(0,0) = 4; A(0,1) = 17;
  A(1,0) = 32; A(1,1) = 33;
  A(2,0) = 63; A(2,1) = 3;
  A(3,0) = 2; A(3,1) = 73;

  Vector<float> b(4);
  b(0) = 5;
  b(1) = 95;
  b(2) = 2;
  b(3) = 1;

  Vector<float> x;

  // Find the least squares solution to the overconstrained problem Ax=b;
  x = least_squares(A,b);

  ASSERT_EQ( 2u, x.size() );
  EXPECT_NEAR( 0.52625521713421449830, x[0], 1e-6 );
  EXPECT_NEAR( 0.37689005929142064427, x[1], 1e-6 );
}

// Test the svd(A,s) routine with a float matrix
TEST(LinearAlgebra, SVDFloat) {
  Matrix<float> A(4,4);
  A(0,0) = 23;  A(0,1) = 1;  A(0,2) = 25;  A(0,3) = 98;
  A(1,0) = 327; A(1,1) = 2;  A(1,2) = 76;  A(1,3) = 66;
  A(2,0) = 234; A(2,1) = 26; A(2,2) = 76;  A(2,3) = 662;
  A(3,0) = 25;  A(3,1) = 62; A(3,2) = 323; A(3,3) = 23;

  Vector<float> s;

  // Find the least squares solution to the overconstrained problem Ax=b;
  svd(A,s);

  ASSERT_EQ( 4u, s.size() );
  EXPECT_NEAR( 744.6187691721427, s[0], 1e-4 );
  EXPECT_NEAR( 336.4697156808418, s[1], 1.1e-4 );
  EXPECT_NEAR( 262.4879718834515, s[2], 1e-4 );
  EXPECT_NEAR( 5.838119793980186, s[3], 1e-4 );

  A = Matrix<float>(3,4);
  A(0,0) = 23;  A(0,1) = 1;  A(0,2) = 25; A(0,3) = 98;
  A(1,0) = 327; A(1,1) = 2;  A(1,2) = 76; A(1,3) = 66;
  A(2,0) = 234; A(2,1) = 26; A(2,2) = 76; A(2,3) = 662;

  // Find the least squares solution to the overconstrained problem Ax=b;
  svd(A,s);

  EXPECT_FLOAT_EQ( 739.852951773669f, s[0] );
  EXPECT_FLOAT_EQ( 282.199549151858f, s[1] );
  EXPECT_FLOAT_EQ( 16.0319746218911f, s[2] );

  A = Matrix<float>(4,3);
  A(0,0) = 23;  A(0,1) = 1;  A(0,2) = 25;
  A(1,0) = 327; A(1,1) = 2;  A(1,2) = 76;
  A(2,0) = 234; A(2,1) = 26; A(2,2) = 76;
  A(3,0) = 25;  A(3,1) = 62; A(3,2) = 323;

  // Find the least squares solution to the overconstrained problem Ax=b;
  svd(A,s);

  EXPECT_FLOAT_EQ( 444.786309525272f  , s[0] );
  EXPECT_FLOAT_EQ( 292.84455931980744f, s[1] );
  EXPECT_FLOAT_EQ( 16.649412472420977f, s[2] );
}

// Test the svd(A,s) routine with a double matrix
TEST(LinearAlgebra, SVDDouble) {
  Matrix<double> A(4,4);
  A(0,0) = 23;  A(0,1) = 1;  A(0,2) = 25;  A(0,3) = 98;
  A(1,0) = 327; A(1,1) = 2;  A(1,2) = 76;  A(1,3) = 66;
  A(2,0) = 234; A(2,1) = 26; A(2,2) = 76;  A(2,3) = 662;
  A(3,0) = 25;  A(3,1) = 62; A(3,2) = 323; A(3,3) = 23;

  Vector<double> s;

  // Find the least squares solution to the overconstrained problem Ax=b;
  svd(A,s);

  EXPECT_NEAR( 744.6187691721427, s[0], 1e-12 );
  EXPECT_NEAR( 336.4697156808418, s[1], 1e-12 );
  EXPECT_NEAR( 262.4879718834515, s[2], 1e-12 );
  EXPECT_NEAR( 5.838119793980186, s[3], 1e-12 );

  A = Matrix<double>(3,4);
  A(0,0) = 23;  A(0,1) = 1;  A(0,2) = 25; A(0,3) = 98;
  A(1,0) = 327; A(1,1) = 2;  A(1,2) = 76; A(1,3) = 66;
  A(2,0) = 234; A(2,1) = 26; A(2,2) = 76; A(2,3) = 662;

  // Find the least squares solution to the overconstrained problem Ax=b;
  svd(A,s);

  EXPECT_NEAR( 739.852951773669, s[0], 1e-12 );
  EXPECT_NEAR( 282.199549151858, s[1], 1e-12 );
  EXPECT_NEAR( 16.0319746218911, s[2], 1e-12 );

  A = Matrix<double>(4,3);
  A(0,0) = 23;  A(0,1) = 1;  A(0,2) = 25;
  A(1,0) = 327; A(1,1) = 2;  A(1,2) = 76;
  A(2,0) = 234; A(2,1) = 26; A(2,2) = 76;
  A(3,0) = 25;  A(3,1) = 62; A(3,2) = 323;

  // Find the least squares solution to the overconstrained problem Ax=b;
  svd(A,s);

  EXPECT_NEAR( 444.786309525272  , s[0], 1e-12 );
  EXPECT_NEAR( 292.84455931980744, s[1], 1e-12 );
  EXPECT_NEAR( 16.649412472420977, s[2], 1e-12 );
}

// Test the svd(A,s) routine with a mixture of float and double matrix/vectors
TEST(LinearAlgebra, SVDDoubleFloat) {
  Matrix<double> A(4,4);
  A(0,0) = 23;  A(0,1) = 1;  A(0,2) = 25;  A(0,3) = 98;
  A(1,0) = 327; A(1,1) = 2;  A(1,2) = 76;  A(1,3) = 66;
  A(2,0) = 234; A(2,1) = 26; A(2,2) = 76;  A(2,3) = 662;
  A(3,0) = 25;  A(3,1) = 62; A(3,2) = 323; A(3,3) = 23;

  Vector<float> s;

  // Find the least squares solution to the overconstrained problem Ax=b;
  svd(A,s);

  EXPECT_NEAR( 744.6187691721427, s[0], 1e-4 );
  EXPECT_NEAR( 336.4697156808418, s[1], 1e-4 );
  EXPECT_NEAR( 262.4879718834515, s[2], 1e-4 );
  EXPECT_NEAR( 5.838119793980186, s[3], 1e-4 );
}

// Test the svd(A,s) routine with a mixture of float and double matrix/vectors
TEST(LinearAlgebra, SVDFloatDouble) {
  Matrix<float> A(4,4);
  A(0,0) = 23;  A(0,1) = 1;  A(0,2) = 25;  A(0,3) = 98;
  A(1,0) = 327; A(1,1) = 2;  A(1,2) = 76;  A(1,3) = 66;
  A(2,0) = 234; A(2,1) = 26; A(2,2) = 76;  A(2,3) = 662;
  A(3,0) = 25;  A(3,1) = 62; A(3,2) = 323; A(3,3) = 23;

  Vector<double> s;

  // Find the least squares solution to the overconstrained problem Ax=b;
  svd(A,s);

  EXPECT_NEAR( 744.6187691721427, s[0], 1e-12 );
  EXPECT_NEAR( 336.4697156808418, s[1], 1e-12 );
  EXPECT_NEAR( 262.4879718834515, s[2], 1e-12 );
  EXPECT_NEAR( 5.838119793980186, s[3], 1e-12 );
}

// Test the svd(A,U,s,VT) routine with a float matrix
TEST(LinearAlgebra, SVDUsVTFloat) {
  Matrix<float> A(4,4);
  A(0,0) = 23;  A(0,1) = 1;  A(0,2) = 25;  A(0,3) = 98;
  A(1,0) = 327; A(1,1) = 2;  A(1,2) = 76;  A(1,3) = 66;
  A(2,0) = 234; A(2,1) = 26; A(2,2) = 76;  A(2,3) = 662;
  A(3,0) = 25;  A(3,1) = 62; A(3,2) = 323; A(3,3) = 23;

  Matrix<float> U, VT;
  Vector<float> s;

  // Find the least squares solution to the overconstrained problem Ax=b;
  svd(A,U,s,VT);

  ASSERT_EQ( A.rows(), U.rows() );
  ASSERT_EQ( std::min(A.rows(), A.cols()), U.cols() );
  ASSERT_EQ( std::min(A.rows(), A.cols()), VT.rows() );
  ASSERT_EQ( A.cols(), VT.cols() );

  Matrix<float,4,4> result = U * diagonal_matrix(s) * VT;
  EXPECT_MATRIX_NEAR(A, result, 1e-3);

  A = Matrix<float>(3,4);
  A(0,0) = 23;  A(0,1) = 1;  A(0,2) = 25; A(0,3) = 98;
  A(1,0) = 327; A(1,1) = 2;  A(1,2) = 76; A(1,3) = 66;
  A(2,0) = 234; A(2,1) = 26; A(2,2) = 76; A(2,3) = 662;

  // Find the least squares solution to the overconstrained problem Ax=b;
  svd(A,U,s,VT);

  ASSERT_EQ( A.rows(), U.rows() );
  ASSERT_EQ( std::min(A.rows(), A.cols()), U.cols() );
  ASSERT_EQ( std::min(A.rows(), A.cols()), VT.rows() );
  ASSERT_EQ( A.cols(), VT.cols() );

  EXPECT_MATRIX_NEAR( A, U*diagonal_matrix(s)*VT, 1e-3 );

  A = Matrix<float>(4,3);
  A(0,0) = 23;  A(0,1) = 1;  A(0,2) = 25;
  A(1,0) = 327; A(1,1) = 2;  A(1,2) = 76;
  A(2,0) = 234; A(2,1) = 26; A(2,2) = 76;
  A(3,0) = 25;  A(3,1) = 62; A(3,2) = 323;

  // Find the least squares solution to the overconstrained problem Ax=b;
  svd(A,U,s,VT);

  ASSERT_EQ( A.rows(), U.rows() );
  ASSERT_EQ( std::min(A.rows(), A.cols()), U.cols() );
  ASSERT_EQ( std::min(A.rows(), A.cols()), VT.rows() );
  ASSERT_EQ( A.cols(), VT.cols() );

// TODO: Fix this on the Mac!
#if (defined(VW_HAVE_PKG_APPLE_LAPACK) && VW_HAVE_PKG_APPLE_LAPACK==1)
#else
  EXPECT_MATRIX_NEAR( A, U * diagonal_matrix(s) * VT, 1.3e-4 );
#endif
}

// Test the qrd(A, Q, R) routine with double matrices
TEST(LinearAlgebra, QRDDouble) {
  Matrix<double> A(4,4), Q(4,4), R(4,4);
  A(0,0) = 23;  A(0,1) = 1; A(0,2) = 25; A(0,3) = 98;
  A(1,0) = 327;  A(1,1) = 2; A(1,2) = 76; A(1,3) = 66;
  A(2,0) = 234;  A(2,1) = 26; A(2,2) = 76; A(2,3) = 662;
  A(3,0) = 25;  A(3,1) = 62; A(3,2) = 323; A(3,3) = 23;

  qrd(A, Q, R);

  EXPECT_NEAR( 1, std::fabs(det(Q)), 1e-13); // Q must be unitary <-> det 1

  // really unitary?
  EXPECT_MATRIX_NEAR( identity_matrix(4), Q*transpose(Q), 1e-13 );
  EXPECT_MATRIX_NEAR( A, Q*R, 1e-12 );

  // R must be upper triangular
  EXPECT_EQ( 0, R(1, 0) );
  EXPECT_EQ( 0, R(2, 1) );
  EXPECT_EQ( 0, R(2, 0) );
  EXPECT_EQ( 0, R(3, 2) );
  EXPECT_EQ( 0, R(3, 1) );
  EXPECT_EQ( 0, R(3, 0) );
}

// Test the qrd(A, Q, R) routine with float matrices
TEST(LinearAlgebra, QRDFloat) {
  Matrix<float> A(4,4), Q(4,4), R(4,4);
  A(0,0) = 23;  A(0,1) = 1; A(0,2) = 25; A(0,3) = 98;
  A(1,0) = 327;  A(1,1) = 2; A(1,2) = 76; A(1,3) = 66;
  A(2,0) = 234;  A(2,1) = 26; A(2,2) = 76; A(2,3) = 662;
  A(3,0) = 25;  A(3,1) = 62; A(3,2) = 323; A(3,3) = 23;

  qrd(A, Q, R);

  EXPECT_NEAR(1, std::fabs(det(Q)), 2.5e-7); // Q must be unitary <-> det 1
  EXPECT_MATRIX_NEAR( identity_matrix(4), Q*transpose(Q), 1e-6 );
  EXPECT_MATRIX_NEAR( A, Q*R, 1e-3);

  // R must be upper triangular
  EXPECT_EQ(R(1, 0), 0);
  EXPECT_EQ(R(2, 1), 0);
  EXPECT_EQ(R(2, 0), 0);
  EXPECT_EQ(R(3, 2), 0);
  EXPECT_EQ(R(3, 1), 0);
  EXPECT_EQ(R(3, 0), 0);
}

// Test the qrd(A, Q, R) routine with double input and float output matrices
TEST(LinearAlgebra, QRDDoubleFloatFloat) {
  Matrix<double> A(4,4);
  Matrix<float> Q(4,4), R(4,4);
  A(0,0) = 23;  A(0,1) = 1;  A(0,2) = 25;  A(0,3) = 98;
  A(1,0) = 327; A(1,1) = 2;  A(1,2) = 76;  A(1,3) = 66;
  A(2,0) = 234; A(2,1) = 26; A(2,2) = 76;  A(2,3) = 662;
  A(3,0) = 25;  A(3,1) = 62; A(3,2) = 323; A(3,3) = 23;

  qrd(A, Q, R);

  EXPECT_NEAR(1, std::fabs(det(Q)), 1e-6); // Q must be unitary <-> det 1
  EXPECT_MATRIX_NEAR( identity_matrix(4), Q*transpose(Q), 1e-6 );
  EXPECT_MATRIX_NEAR( A, Q*R, 1e-4 );

  // R must be upper triangular
  EXPECT_EQ(R(1, 0), 0);
  EXPECT_EQ(R(2, 1), 0);
  EXPECT_EQ(R(2, 0), 0);
  EXPECT_EQ(R(3, 2), 0);
  EXPECT_EQ(R(3, 1), 0);
  EXPECT_EQ(R(3, 0), 0);
}

// Test the rqd(A, R, Q) routine with double matrices
TEST(LinearAlgebra, RQDDouble) {
  Matrix<double> A(4,4), Q(4,4), R(4,4);
  A(0,0) = 23;  A(0,1) = 1;  A(0,2) = 25;  A(0,3) = 98;
  A(1,0) = 327; A(1,1) = 2;  A(1,2) = 76;  A(1,3) = 66;
  A(2,0) = 234; A(2,1) = 26; A(2,2) = 76;  A(2,3) = 662;
  A(3,0) = 25;  A(3,1) = 62; A(3,2) = 323; A(3,3) = 23;

  rqd(A, R, Q);

  EXPECT_NEAR(1, std::fabs(det(Q)), 1e-15); // Q must be unitary <-> det 1
  EXPECT_MATRIX_NEAR( identity_matrix(4), Q*transpose(Q), 1e-15 );
  EXPECT_MATRIX_NEAR( A, R*Q, 1e-12 );

  // R must be upper triangular
  EXPECT_EQ(R(1, 0), 0);
  EXPECT_EQ(R(2, 1), 0);
  EXPECT_EQ(R(2, 0), 0);
  EXPECT_EQ(R(3, 2), 0);
  EXPECT_EQ(R(3, 1), 0);
  EXPECT_EQ(R(3, 0), 0);
}

// Test the rqd(A, R, Q) routine with float matrices
TEST(LinearAlgebra, RQDFloat) {
  Matrix<float> A(4,4), Q(4,4), R(4,4);
  A(0,0) = 23;  A(0,1) = 1; A(0,2) = 25; A(0,3) = 98;
  A(1,0) = 327;  A(1,1) = 2; A(1,2) = 76; A(1,3) = 66;
  A(2,0) = 234;  A(2,1) = 26; A(2,2) = 76; A(2,3) = 662;
  A(3,0) = 25;  A(3,1) = 62; A(3,2) = 323; A(3,3) = 23;

  rqd(A, R, Q);

  EXPECT_NEAR(1, std::fabs(det(Q)), 1e-6); // Q must be unitary <-> det 1
  EXPECT_MATRIX_NEAR( identity_matrix(4), Q*transpose(Q), 1e-6 );
  EXPECT_MATRIX_NEAR( A, R*Q, 1e-3 );

  // R must be upper triangular
  EXPECT_EQ(R(1, 0), 0);
  EXPECT_EQ(R(2, 1), 0);
  EXPECT_EQ(R(2, 0), 0);
  EXPECT_EQ(R(3, 2), 0);
  EXPECT_EQ(R(3, 1), 0);
  EXPECT_EQ(R(3, 0), 0);
}

// Test the rqd(A, R, Q) routine with double input and float output matrices
TEST(LinearAlgebra, RQDDoubleFloatFloat) {
  Matrix<double> A(4,4);
  Matrix<float> Q(4,4), R(4,4);
  A(0,0) = 23;  A(0,1) = 1; A(0,2) = 25; A(0,3) = 98;
  A(1,0) = 327;  A(1,1) = 2; A(1,2) = 76; A(1,3) = 66;
  A(2,0) = 234;  A(2,1) = 26; A(2,2) = 76; A(2,3) = 662;
  A(3,0) = 25;  A(3,1) = 62; A(3,2) = 323; A(3,3) = 23;

  rqd(A, R, Q);

  EXPECT_NEAR(1, std::fabs(det(Q)), 1e-20); // Q must be unitary <-> det 1
  EXPECT_MATRIX_NEAR( identity_matrix(4), Q*transpose(Q), 1e-7 );
  EXPECT_MATRIX_NEAR( A, R*Q, 1e-4 );

  // R must be upper triangular
  EXPECT_EQ(R(1, 0), 0);
  EXPECT_EQ(R(2, 1), 0);
  EXPECT_EQ(R(2, 0), 0);
  EXPECT_EQ(R(3, 2), 0);
  EXPECT_EQ(R(3, 1), 0);
  EXPECT_EQ(R(3, 0), 0);
}

// Test the pseudoinverse routine with a float matrix
TEST(LinearAlgebra, PseudoInverse) {
  Matrix<float> A(4,4);
  A(0,0) = 23;  A(0,1) = 1;  A(0,2) = 25;  A(0,3) = 98;
  A(1,0) = 327; A(1,1) = 2;  A(1,2) = 76;  A(1,3) = 66;
  A(2,0) = 234; A(2,1) = 26; A(2,2) = 76;  A(2,3) = 662;
  A(3,0) = 25;  A(3,1) = 62; A(3,2) = 323; A(3,3) = 23;


  Matrix<float> Ap = pseudoinverse(A);

  // moore-penrose pseudoinverse criteria
  EXPECT_MATRIX_NEAR( A,    A*Ap*A,          1e-3 );
  EXPECT_MATRIX_NEAR( Ap,   Ap*A*Ap,         1e-6 );
  EXPECT_MATRIX_NEAR( A*Ap, transpose(A*Ap), 1e-4 );
  EXPECT_MATRIX_NEAR( Ap*A, transpose(Ap*A), 1e-4 );

  float Ap_expect_[16] = {
      -0.007535090380630f, 0.003022093679622f, 0.000825360012021f,-0.000322071891793f,
      -0.165749521270461f,-0.006829837951081f, 0.024920026123725f, 0.008572395450900f,
       0.032007360625223f, 0.001143394805474f,-0.004904483243026f, 0.001503587323911f,
       0.005498699840759f,-0.000931257008338f, 0.000803150761994f,-0.000395453316709f
  };

  EXPECT_MATRIX_NEAR( (MatrixProxy<float>(Ap_expect_, 4, 4)), Ap, 1e-6 );

  A = Matrix<float>(3,4);
  A(0,0) = 23;  A(0,1) = 1; A(0,2) = 25; A(0,3) = 98;
  A(1,0) = 327;  A(1,1) = 2; A(1,2) = 76; A(1,3) = 66;
  A(2,0) = 234;  A(2,1) = 26; A(2,2) = 76; A(2,3) = 662;

  Ap = pseudoinverse(A);

  // moore-penrose pseudoinverse criteria
  EXPECT_MATRIX_NEAR( A,    A*Ap*A,          1e-3 );
  EXPECT_MATRIX_NEAR( Ap,   Ap*A*Ap,         1e-7 );
  EXPECT_MATRIX_NEAR( A*Ap, transpose(A*Ap), 1e-5 );
  EXPECT_MATRIX_NEAR( Ap*A, transpose(Ap*A), 1e-5 );

  float Ap_expect2_[12] = {
    -0.013350897144624f,  0.002778722755045f,  0.001696852752391f,
    -0.010953661347906f, -0.000352179205052f,  0.001724023636673f,
     0.059158358745482f,  0.002279567959751f, -0.008973032752286f,
    -0.001642190540437f, -0.001230077991375f,  0.001873205937403f
  };

  ASSERT_EQ( A.cols(), Ap.rows() );
  ASSERT_EQ( A.rows(), Ap.cols() );
  EXPECT_MATRIX_NEAR( MatrixProxy<float>(Ap_expect2_, 4, 3), Ap, 1e-7 );

  A = Matrix<float>(4,3);
  A(0,0) = 23;  A(0,1) = 1; A(0,2) = 25;
  A(1,0) = 327;  A(1,1) = 2; A(1,2) = 76;
  A(2,0) = 234;  A(2,1) = 26; A(2,2) = 76;
  A(3,0) = 25;  A(3,1) = 62; A(3,2) = 323;

  Ap = pseudoinverse(A);

#if (defined(VW_HAVE_PKG_APPLE_LAPACK) && VW_HAVE_PKG_APPLE_LAPACK==1)
#else
  // moore-penrose pseudoinverse criteria
  EXPECT_MATRIX_NEAR( A,    A*Ap*A,          1.1e-4 );
  EXPECT_MATRIX_NEAR( Ap,   Ap*A*Ap,         1e-8 );
  EXPECT_MATRIX_NEAR( A*Ap, transpose(A*Ap), 1e-5 );
  EXPECT_MATRIX_NEAR( Ap*A, transpose(Ap*A), 1e-5 );

  float Ap_expect3_[12] = {
    -0.000045251795784f,  0.001753618426299f, 0.001919340360256f,-0.000860723135862f,
    -0.012630523420775f, -0.032761993549060f, 0.047284883882862f,-0.002439555974766f,
     0.002638947028891f,  0.006117214395082f,-0.009194090685064f, 0.003615693257945f
  };

  ASSERT_EQ( A.cols(), Ap.rows() );
  ASSERT_EQ( A.rows(), Ap.cols() );
  EXPECT_MATRIX_NEAR( MatrixProxy<float>(Ap_expect3_, 3, 4), Ap, 1e-7);
#endif
}

typedef std::complex<double> cdouble;

static bool less_cdouble(cdouble const& a, cdouble const& b)
{
  return std::make_pair(a.real(), a.imag()) < std::make_pair(b.real(), b.imag());
}

// Test the eigen(A,e) routine with a double matrix
TEST(LinearAlgebra, Eigen) {
  Matrix<double> A(4,4);
  A(0,0) = 23;  A(0,1) = 1;  A(0,2) = 25;  A(0,3) = 98;
  A(1,0) = 327; A(1,1) = 2;  A(1,2) = 76;  A(1,3) = 66;
  A(2,0) = 234; A(2,1) = 26; A(2,2) = 76;  A(2,3) = 662;
  A(3,0) = 25;  A(3,1) = 62; A(3,2) = 323; A(3,3) = 23;

  Vector<cdouble> e;
  Matrix<cdouble> V;

  // Find the least squares solution to the overconstrained problem Ax=b;
  eigen(A,e);

  // Values are returned in no particular order
  std::sort(e.begin(), e.end(), less_cdouble);

  EXPECT_NEAR( e(0).real(), -397.9430935051340, 1e-11 );
  EXPECT_NEAR( e(0).imag(),    0.0,             1e-11 );
  EXPECT_NEAR( e(1).real(),  -16.4778368282945, 1e-11 );
  EXPECT_NEAR( e(1).imag(),  -38.3039533150042, 1e-11 );
  EXPECT_NEAR( e(2).real(),  -16.4778368282945, 1e-11 );
  EXPECT_NEAR( e(2).imag(),   38.3039533150042, 1e-11 );
  EXPECT_NEAR( e(3).real(),  554.8987671617240, 1e-11 );
  EXPECT_NEAR( e(3).imag(),    0.0,             1e-11 );

  eigen(A,e,V);

  EXPECT_COMPLEX_MATRIX_NEAR( A*V, V*diagonal_matrix(e), 1e-12 );
}

TEST(LinearAlgebra, Static) {
  Matrix<float,3,3> A;
  A(0,0) = 81; A(0,1) = 91; A(0,2) = 27;
  A(1,0) = 90; A(1,1) = 63; A(1,2) = 54;
  A(2,0) = 12; A(2,1) = 9;  A(2,2) = 95;

  Vector<float,3> b;
  b(0) = 5;
  b(1) = 95;
  b(2) = 2;

  Vector<float,3> x = solve(A,b);

  EXPECT_VECTOR_NEAR( b, A*x, 1e-5 );

  EXPECT_NEAR( 764269./272205, x(0), 1e-6 );
  EXPECT_NEAR( -72983./30245,  x(1), 1e-6 );
  EXPECT_NEAR( -9527./90735,   x(2), 1e-6 );
}

TEST(LinearAlgebra, SymmetricStatic) {
  Matrix<float,3,3> A_;
  A_(0,0) = 81; A_(0,1) = 91; A_(0,2) = 27;
  A_(1,0) = 90; A_(1,1) = 63; A_(1,2) = 54;
  A_(2,0) = 12; A_(2,1) = 9;  A_(2,2) = 95;

  Matrix<float,3,3> A = transpose(A_)*A_;

  Vector<float,3> b;
  b(0) = 5;
  b(1) = 95;
  b(2) = 2;

  Matrix<float,3,3> Lt = A;
  Vector<float,3>   x = b;

  solve_symmetric_modify(Lt,x);

  // it comes back transposed due to fortran
  Matrix<float,3,3> L = transpose(Lt);

  // This fast version does not zero the upper triangular part. Do so.
  L(0,1) = 0;
  L(0,2) = 0;
  L(1,2) = 0;

  // Check that answer is correct
  EXPECT_VECTOR_NEAR( b, A*x, 1e-3 );

  // Check Cholesky factorization (lower-triangular)
  EXPECT_MATRIX_FLOAT_EQ( A, L * transpose(L) );

  float Lexpect_[9] = {
    121.675798744039000f,   0.000000000000000f,  0.000000000000000f,
    108.065861376925000f,  25.549356251444400f,  0.000000000000000f,
     67.285360642853800f, -21.810743505349500f, 87.561245485117200f
  };
  Matrix<float,3,3> Lexpect(Lexpect_);

  EXPECT_MATRIX_NEAR( Lexpect, L, 1e-4 );

  // Check exact values
  EXPECT_NEAR( -0.135965535811725 , x(0), 1e-6 );
  EXPECT_NEAR( 0.147253418731322  , x(1), 1e-6 );
  EXPECT_NEAR( 0.009983456063811  , x(2), 1e-6 );

  Matrix<float,3,2> B;
  B(0,0) = 5; B(1,0) = 95; B(2,0) = 2;
  B(0,1) = 5; B(1,1) = 95; B(2,1) = 2;

  Matrix<float> X;
  X = multi_solve_symmetric(A,B);

  EXPECT_MATRIX_NEAR( B, A*X, 1e-3 );
}

TEST(LinearAlgebra, RankAndNullspace) {
  // Square Matrix
  Matrix<double> magic(3,3);
  magic(0,0) = 8; magic(0,1) = 1; magic(0,2) = 6;
  magic(1,0) = 3; magic(1,1) = 5; magic(1,2) = 7;
  magic(2,0) = 4; magic(2,1) = 9; magic(2,2) = 2;

  Matrix<double> nullsp = nullspace(magic);
  EXPECT_EQ( 0u, nullsp.cols() );
  EXPECT_EQ( 0u, nullsp.rows() );
  EXPECT_EQ( 3,  rank(magic) );  // It's square yo
  EXPECT_EQ( 0u, nullity(magic) );

  { // Common example of nullspace
    Matrix<double> cow(2,4);
    cow(0,0) = -1; cow(0,1) = 1; cow(0,2) = 2; cow(0,3) = 4;
    cow(1,0) = 2;  cow(1,1) = 0; cow(1,2) = 1; cow(1,3) = -7;

    nullsp = nullspace(cow);
    EXPECT_EQ( nullsp.cols(), 2u );
    EXPECT_EQ( nullsp.rows(), 4u );

    Vector<double> definition_check;
    definition_check = cow*select_col(nullsp,0);
    for ( uint32 i = 0; i < definition_check.size(); i++ )
      EXPECT_NEAR( 0, definition_check(i), 1e-8 );
    definition_check = cow*select_col(nullsp,1);
    for ( uint32 i = 0; i < definition_check.size(); i++ )
      EXPECT_NEAR( 0, definition_check(i), 1e-8 );

    EXPECT_EQ( 2,  rank(cow) );
    EXPECT_EQ( 2u, nullity(cow) );
  }

  { // Data that threw an error in the past
    float monkey_data[63] = {
      148291,148852,187,537654,539688,678,793,796,1,
      1.60418e+06,958416,974,1.67335e+06,999744,1016,1647,984,1,
      1.09336e+06,835077,753,1.5972e+06,1.2199e+06,1100,1452,1109,1,
      371148,156024,394,303324,127512,322,942,396,1,
      94659,64357,139,234945,159735,345,681,463,1,
      945260,125330,755,192808,25564,154,1252,166,1,
      391777,206916,401,430857,227556,441,977,516,1
    };

    MatrixProxy<float> monkey( monkey_data, 7, 9 );
    nullsp = nullspace(monkey);
    EXPECT_EQ( 2u, nullsp.cols() );
    EXPECT_EQ( 9u, nullsp.rows() );
    EXPECT_EQ( 7, rank(monkey) );
    EXPECT_EQ( 2u, nullity(monkey) );

    Vector<double> definition_check;
    definition_check = monkey*select_col(nullsp,0);
    for ( uint32 i = 0; i < definition_check.size(); i++ )
      EXPECT_NEAR( 0, definition_check(i), 1e-1 );
    definition_check = monkey*select_col(nullsp,1);
    for ( uint32 i = 0; i < definition_check.size(); i++ )
      EXPECT_NEAR( 0, definition_check(i), 1e-1 );

    nullsp = nullspace(transpose(monkey));
    EXPECT_EQ( 0u, nullsp.cols() );
    EXPECT_EQ( 0u, nullsp.rows() );
    EXPECT_EQ( 7, rank(transpose(monkey)) );
    EXPECT_EQ( 0u, nullity(transpose(monkey)) );
  }

  {  // More Data that threw an error in the past
    double shark_data[63] = {
      1.36483e+06,846250,1354,687456,426250,682,1008,625,1,
      1.28992e+06,750141,1323,607425,353241,623,975,567,1,
      1.2395e+06,710892,1302,572152,328146,601,952,546,1,
      1.26153e+06,792550,1310,636543,399905,661,963,605,1,
      1.23405e+06,740430,1299,593750,356250,625,950,570,1,
      1.22094e+06,799748,1292,637875,417825,675,945,619,1,
      1.15748e+06,708400,1265,562725,344400,615,915,560,1
    };

    MatrixProxy<double> shark( shark_data, 7, 9 );
    nullsp = nullspace(shark);
    EXPECT_EQ( nullsp.cols(), 2u );
    EXPECT_EQ( nullsp.rows(), 9u );

    Vector<double> definition_check;
    definition_check = shark*select_col(nullsp,0);
    for ( uint32 i = 0; i < definition_check.size(); i++ )
      EXPECT_NEAR( 0, definition_check(i), 1e-5 );
    definition_check = shark*select_col(nullsp,1);
    for ( uint32 i = 0; i < definition_check.size(); i++ )
      EXPECT_NEAR( 0, definition_check(i), 1e-5 );

    EXPECT_EQ( 7, rank(shark) );
    EXPECT_EQ( 2u, nullity(shark) );
  }
}

TEST(LinearAlgebra, LU_Decomposition) {
  {
    Matrix<float> a(3,3);
    a(0,0) = 1; a(0,1) = 2; a(0,2) = 3;
    a(1,0) = 4; a(1,1) = 5; a(1,2) = 6;
    a(2,0) = 7; a(2,1) = 8; a(2,2) = 0;

    LUD<float> decomposition(a);

    // if X = [4;5;6]
    Vector3f b1(32,77,68);
    // if X = [1.7;-3.9;0.3]
    Vector3f b2(-5.2,-10.9,-19.3);

    double tol = 1e-4;
    Vector<float> result = decomposition.solve(b1);
    ASSERT_EQ( 3u, result.size() );
    EXPECT_VECTOR_NEAR( Vector3f(4,5,6), result, tol );
    result = decomposition.solve(b2);
    ASSERT_EQ( 3u, result.size() );
    EXPECT_VECTOR_NEAR( Vector3f(1.7,-3.9,0.3), result, tol );

    // Try again with doubles
    Matrix<double> ad = a;
    LUD<double> decompositiond(ad);
    Vector<double> resultd = decompositiond.solve(b1);
    ASSERT_EQ( resultd.size(), 3u );
    EXPECT_VECTOR_NEAR( Vector3(4,5,6), resultd, tol );
    resultd = decompositiond.solve(b2);
    ASSERT_EQ( resultd.size(), 3u );
    EXPECT_VECTOR_NEAR( Vector3(1.7,-3.9,0.3), resultd, tol );

    // Check for singular
    a(2,2) = 9;
    EXPECT_THROW( LUD<float> monkey(a),
                  ArgumentErr );
  }
  {
    // Test Non Square Error
    Matrix<double> a(4,3);
    EXPECT_THROW( LUD<double> monkey(a),
                  ArgumentErr );
  }
}
